Skip to content

Conversation

paulmialane
Copy link

@paulmialane paulmialane commented Oct 8, 2025

Add a config option inherent-impl-lint-scope to the lint multiple_inherent_impl to target a different scope according to people's needs. It can take three values: module, file, and crate (default).

  • module is the weakest option. It lints if there are two or more impls in the same module.
  • file is a bit stronger, since it lints if there are two or more impls in the same file. So, this triggers the lint (where it did not with module):
  • crate is the strongest of them; it triggers as soon as there are two or more impls anywhere in the crate. It is the current behaviour of the lint, so it's the default option.

changelog: [multiple_inherent_impl] : Add config option (module, file or crate) to target specific scope

fixes #14867

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Oct 8, 2025
@rustbot
Copy link
Collaborator

rustbot commented Oct 8, 2025

r? @samueltardieu

rustbot has assigned @samueltardieu.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Copy link
Member

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution. There are some things that need to be changed in the way the configuration option is parsed (and output in messages) so that lower-case versions "crate", "file", and "module" can be used instead of a mixed-case one.

View changes since this review

///
/// The config option controls the scope at which multiple inherent impl blocks for the same
/// struct are linted, allowing values of "module" (only within the same module), "file"
/// (within the same file), or "crate" (anywhere in the crate, default).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those lowercase values are not currently recognized (yet), see other comment.

struct T;

impl S {
fn fisrt() {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo? (several occurrences)

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Oct 8, 2025
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties and removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Oct 8, 2025
Copy link
Member

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. Here are a new round of comments, the PR is starting to look good.

View changes since this review

/// A list of paths to types that should be treated as if they do not contain interior mutability
#[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)]
ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()]),
/// Sets the scope at which duplicate impl blocks for the same type are linted.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit

Suggested change
/// Sets the scope at which duplicate impl blocks for the same type are linted.
/// Sets the scope ("crate", "file", or "module") in which duplicate inherent `impl` blocks for the same type are linted.

Entry::Vacant(e) => {
// Store the id for the first impl block of this type. The span is retrieved lazily.
e.insert(IdOrSpan::Id(impl_id));
e.insert((IdOrSpan::Id(impl_id), criterion.clone()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be no need to insert the criterion as part of the value (while it is useful as part of the key). Can't you just keep inserting the IdOrSpan as was done before? Also, this would let you remove the .clone() and write match type_map.entry((impl_ty, criterion)) { above.

Entry::Occupied(mut e) => {
if let Some(span) = get_impl_span(cx, impl_id) {
let first_span = match *e.get() {
let first_span = match e.get().0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also restore the dereference once you remove the criterion.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Oct 8, 2025
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties and removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Oct 9, 2025
Copy link
Member

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also please squash the commits?

View changes since this review

Comment on lines 98 to 109
if let Node::Item(&Item {
kind: ItemKind::Impl(_impl_id),
span,
..
}) = cx.tcx.hir_node(hir_id)
{
Criterion::File(cx.tcx.sess.source_map().lookup_source_file(span.lo()).name.clone())
} else {
// We know we are working on an impl, so the pattern matching can
// not fail
unreachable!()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if let Node::Item(&Item {
kind: ItemKind::Impl(_impl_id),
span,
..
}) = cx.tcx.hir_node(hir_id)
{
Criterion::File(cx.tcx.sess.source_map().lookup_source_file(span.lo()).name.clone())
} else {
// We know we are working on an impl, so the pattern matching can
// not fail
unreachable!()
}
let span = cx.tcx.hir_span(hir_id);
Criterion::File(cx.tcx.sess.source_map().lookup_source_file(span.lo()).name.clone())

Comment on lines 84 to 87
|| is_lint_allowed(
cx,
MULTIPLE_INHERENT_IMPL,
cx.tcx.local_def_id_to_hir_id(id),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will not work if #[expect] is used on the type definition, as the expectation will not be fulfilled. It would probably be better to call clippy_utils::fulfill_or_allowed(…). This must be done only when you know for sure that the lint would be emitted if not allowed or expected.

Could you add some tests with #[expect] to check that this works as expected (ah ah)?

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Oct 9, 2025
@paulmialane
Copy link
Author

I need help to understand what's wrong with my test file.

I looked at how #[expect(...)] works, and as suggested I tried using fulfill_or_allowed(). I think I have the desired behaviour, as when I test on my own crate using cargo dev lint ... I have the expected errors.

But, when I use cargo uibless on my new test file:

error: diagnostic code `clippy::unfulfilled_lint_expectations` not found on line 80
  --> tests/ui/impl.rs:81:6
   |
81 | //~^ unfulfilled_lint_expectations
   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected because of this pattern
   |

error: there were 1 unmatched diagnostics
  --> tests/ui/impl.rs:80:10
   |
80 | #[expect(clippy::multiple_inherent_impl)]
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Error[unfulfilled_lint_expectations]: this lint expectation is unfulfilled

I don't understand why I get this... The way I wrote it is probably not the intended way, but then how?

Thank you!

@paulmialane paulmialane force-pushed the feat/multiple_inherent_impl branch from 21e0f33 to 97de6e0 Compare October 10, 2025 16:53
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties and removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Oct 10, 2025
Copy link
Member

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like only a minor comment change is necessary to ensure consistency.

I also find the IdOrSpan construct a bit useless, but that is not on you as this is already present. I have plans to remove this later once your PR is merged, and do some cleanup in the way this lint works, unrelated to your changes.

View changes since this review


for (&id, impl_ids) in &impls.inherent_impls {
if impl_ids.len() < 2
// Check for `#[allow]` on the type definition
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Check for `#[allow]` on the type definition
// Check for `#[expect]` or `#[allow]` on the type definition

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Oct 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lint suggestion: duplicate_impl_blocks

3 participants